package org.example.infrastructure.persistent.repository;

import lombok.extern.slf4j.Slf4j;
import org.example.domain.activity.model.entity.ActivityEntity;
import org.example.domain.strategy.model.Raffle.StrategyAwardEntity;
import org.example.domain.strategy.model.Raffle.StrategyEntity;
import org.example.domain.strategy.model.Raffle.StrategyRuleEntity;
import org.example.domain.strategy.model.valobj.*;
import org.example.domain.strategy.repository.IStrategyRepository;
import org.example.domain.strategy.service.rule.Chain.factory.DefaultLogicChainFactory;
import org.example.infrastructure.persistent.dao.*;
import org.example.infrastructure.persistent.po.*;
import org.example.infrastructure.persistent.redis.IRedisService;
import org.example.types.common.Constants;
import org.redisson.api.*;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Repository
@Slf4j
public class StrategyRepository  implements IStrategyRepository {

    @Resource
    private IStrategyAwardDao strategyAwardDao;

    @Resource
    private IStrategyDao strategyDao;

    @Resource
    private IStrategyRuleDao strategyRuleDao;

    @Resource
    private IRuleTreeDao ruleTreeDao;

    @Resource
    private IRuleTreeNodeDao ruleTreeNodeDao;

    @Resource
    private IRuleTreeNodeLineDao ruleTreeNodeLineDao;

    @Resource
    private IRaffleActivityDao raffleActivityDao;

    @Resource
    private IRaffleActivityDao activityDao;

    @Resource
    private IRaffleActivityAccountDayDao raffleActivityAccountDayDao;

    @Resource
    private IRaffleActivityAccountDao raffleActivityAccountDao;

    @Resource
    private IRedisService redisService;
    @Override
    public List<StrategyAwardEntity> queryStrategyAwardList(Long strategyId) {

        List<StrategyAward> strategyAwardList =strategyAwardDao.queryStrategyAwardListByStrategyId(strategyId);

        List<StrategyAwardEntity>  strategyAwardEntityList = new ArrayList<>();
        for (StrategyAward strategyAward:strategyAwardList){
            StrategyAwardEntity strategyAwardEntity = StrategyAwardEntity.builder()
                        .strategyId(strategyAward.getStrategyId())
                        .awardId(strategyAward.getAwardId())
                        .ruleModels(strategyAward.getRuleModels())
                        .awardCount(strategyAward.getAwardCount())
                        .awardCountSurplus(strategyAward.getAwardCountSurplus())
                        .awardRate(strategyAward.getAwardRate())
                        .awardTitle(strategyAward.getAwardTitle())
                        .awardSubtitle(strategyAward.getAwardSubtitle())
                        .sort(strategyAward.getSort())
                        .build();

            strategyAwardEntityList.add(strategyAwardEntity);
        }
        return strategyAwardEntityList;
    }


    @Override
    public void storeStrategyAwardSearchRateTable(String key,Integer rateRange, Map strategyAwardSearchRateTablesMap) {

        //存储策略范围值,别人靠这个生成随机数
        redisService.setValue(Constants.RedisKey.STRATEGY_RATE_RANGE_KEY + key,rateRange );

        //存储策略查找表                                                                                       1
        RMap<Integer,Integer> cacheRateMap= redisService.getMap(Constants.RedisKey.STRATEGY_RATE_TABLE_KEY +key) ;


        cacheRateMap.putAll(strategyAwardSearchRateTablesMap);


    }

    @Override
    public Integer getRateRangeByStrategyId(Long strategyId) {
        return redisService.getValue(Constants.RedisKey.STRATEGY_RATE_RANGE_KEY+strategyId);
    }

    @Override
    public int getRateRange(String key) {
        return redisService.getValue(Constants.RedisKey.STRATEGY_RATE_RANGE_KEY+key);
    }

    @Override
    public Integer getAwardIdByRandomIndex(Long strategyId,Integer randomIndex ) {
        return redisService.getFromMap(Constants.RedisKey.STRATEGY_RATE_TABLE_KEY + strategyId, randomIndex);
    }

    @Override
    public Integer getAwardId(String key, Integer randomIndex) {
        return redisService.getFromMap(Constants.RedisKey.STRATEGY_RATE_TABLE_KEY + key, randomIndex);
    }

    @Override
    public StrategyAwardRuleModelVO queryStrategyAwardRuleModelVO(Long strategyId, Integer awardId) {
       StrategyAward strategyAward = strategyAwardDao.queryStrategyAwardByStrategyIdAndAwardId(Long.valueOf(strategyId),awardId);
       if (strategyAward == null) return null;
       String ruleModels = strategyAward.getRuleModels();

        return StrategyAwardRuleModelVO.builder().ruleModels(ruleModels).build();
    }

    @Override
    public StrategyRuleEntity queryStrategyRuleEntityByStrategyIdAndRuleModelAndAwardId(String strategyId, String ruleModel, String awardId) {
        StrategyRule strategyRule = strategyRuleDao.queryStrategyRuleByStrategyIdAndRuleModelAndAwardId(strategyId,ruleModel,awardId);

        StrategyRuleEntity strategyRuleEntity = StrategyRuleEntity.builder()
                .strategyId(strategyRule.getStrategyId())
                .awardId(strategyRule.getAwardId())
                .ruleType(strategyRule.getRuleType())
                .ruleModel(strategyRule.getRuleModel())
                .ruleValue(strategyRule.getRuleValue())
                .ruleDesc(strategyRule.getRuleDesc())
                .build();

        return strategyRuleEntity;
    }



    @Override
    public StrategyEntity queryStrategyEntityByStrategyId(Long strategyId) {
        String cacheKey = Constants.RedisKey.STRATEGY_KEY + strategyId;
        //高版本fastjson不能自动将json转换为Java对象,必须要这样转换
      //  JSONObject jsonObjectCacheStrategyEntity = redisService.getValue(cacheKey);
//        StrategyEntity strategyEntity=redisService.getValue(cacheKey);
//        if (strategyEntity != null) return strategyEntity;
      //  if (jsonObjectCacheStrategyEntity != null) return jsonObjectCacheStrategyEntity.toJavaObject(StrategyEntity.class);

        Strategy strategy = strategyDao.queryStrategyByStrategyId(strategyId);


        StrategyEntity strategyEntity = StrategyEntity.builder()
                .strategyId(strategy.getStrategyId())
                .strategyDesc(strategy.getStrategyDesc())
                .ruleModels(strategy.getRuleModels())
                .build();

        redisService.setValue(cacheKey,strategyEntity);
        return strategyEntity;
    }



    @Override
    public StrategyRuleEntity queryStrategyRuleEntityByStrategyIdAndRuleModel(Long strategyId, String ruleModel) {



        StrategyRule strategyRule =strategyRuleDao.queryStrategyRuleByStrategyIdAndRuleModel(strategyId,ruleModel);

        StrategyRuleEntity strategyRuleEntity = StrategyRuleEntity.builder()
                .strategyId(strategyRule.getStrategyId())
                .awardId(strategyRule.getAwardId())
                .ruleType(strategyRule.getRuleType())
                .ruleModel(strategyRule.getRuleModel())
                .ruleValue(strategyRule.getRuleValue())
                .ruleDesc(strategyRule.getRuleDesc())
                .build();

        return strategyRuleEntity;
    }





    @Override
    public RuleTreeVO queryRuleTreeVOByTreeId(String treeId) {

        List<RuleTreeNode> ruleTreeNodeList = ruleTreeNodeDao.queryRuleTreeNodeListByTreeId(treeId);

        //1.将List<RuleTreeNode>转变为List<RuleTreeNodeVO>
        List<RuleTreeNodeVO> ruleTreeNodeVOList = new ArrayList<>();
        for (RuleTreeNode ruleTreeNode : ruleTreeNodeList) {
            RuleTreeNodeVO ruleTreeNodeVO = RuleTreeNodeVO
                    .builder()
                    .nodeKey(ruleTreeNode.getRuleKey())
                    .ruleValue(ruleTreeNode.getRuleValue())
                    .build();

            ruleTreeNodeVOList.add(ruleTreeNodeVO);

        }


        //转变为nodeVO对应的map
        Map<String,RuleTreeNodeVO> ruleTreeNodeVOMap = new HashMap<>();
        //2.根据List<RuleTreeNodeVO>每个元素的nodeKey和treeId在数据库中查找对应的List<RuleTreeNodeLine>
        for (RuleTreeNodeVO ruleTreeNodeVO : ruleTreeNodeVOList) {
            String ruleNodeFrom = ruleTreeNodeVO.getNodeKey();
            List<RuleTreeNodeLine> ruleTreeNodeLineList  = ruleTreeNodeLineDao.queryRuleTreeNodeLineListByTreeId(treeId,ruleNodeFrom);

            //3.将查到的List<RuleTreeNodeLine>转为List<RuleTreeNodeLineVO>
            List<RuleTreeNodeLineVO> ruleTreeNodeLineVOList = new ArrayList<>();
            for (RuleTreeNodeLine ruleTreeNodeLine : ruleTreeNodeLineList){
                RuleTreeNodeLineVO ruleTreeNodeLineVO =RuleTreeNodeLineVO
                        .builder()
                        .fromNodeKey(ruleTreeNodeLine.getRuleNodeFrom())
                        .toNodeKey(ruleTreeNodeLine.getRuleNodeTo())
                        .limitType(RuleLimitTypeVO.valueOf(ruleTreeNodeLine.getRuleLimitType()))
                        .limitValue(RuleLimitValueVO.valueOf(ruleTreeNodeLine.getRuleLimitValue()))
                        .build();
                ruleTreeNodeLineVOList.add(ruleTreeNodeLineVO);
            }

            //4.给每个RuleTreeNodeVO元素添加对应的List<RuleTreeNodeLineVO>
            ruleTreeNodeVO.setRuleTreeNodeLineVOList(ruleTreeNodeLineVOList);
            //将nodeVO转为map
            ruleTreeNodeVOMap.put(ruleTreeNodeVO.getNodeKey(),ruleTreeNodeVO);

        }

        //将RuleTree转变为将TreeNodeVO,并将map传进去
        RuleTree ruleTree = ruleTreeDao.queryRuleTreeByTreeId(treeId);
        RuleTreeVO ruleTreeVO = RuleTreeVO.builder()
                .ruleTreeId((ruleTree.getTreeId()))
                .rootNodeKey(ruleTree.getTreeNodeRuleKey())
                .ruleTreeDesc(ruleTree.getTreeDesc())
                .ruleTreeNodeVOMap(ruleTreeNodeVOMap)
                .build();

        return ruleTreeVO;






        }

    @Override
    public void cacheStrategyAwardCount(String cacheKey, Long awardCount) {
        if (redisService.isExists(cacheKey)) return;
        redisService.setAtomicLong(cacheKey, awardCount);
    }

    @Override
    public boolean subtractionAwardStockInRedis(String key) {
       return subtractionAwardStockInRedis(key,null);
    }
    //Todo:
    @Override
    public boolean subtractionAwardStockInRedis(String key, Date endDateTime) {
        Long surplus =  redisService.decr(key);
        if (surplus < 0) {
            // 库存小于0，恢复为0个
            redisService.setValue(key, 0);
            return false;
        }

        // 1. 按照cacheKey decr 后的值，如 99、98、97 和 key 组成为库存锁的key进行使用。
        // 2. 加锁为了兜底，如果后续有恢复库存，手动处理等，也不会超卖。因为所有的可用库存key，都被加锁了。
        String lockKey = key + Constants.UNDERLINE + surplus;
        Boolean lock = false;
        if (null != endDateTime) {
            long expireMillis = endDateTime.getTime() - System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1);
            lock = redisService.setNx(lockKey, expireMillis, TimeUnit.MILLISECONDS);
        } else {
            lock = redisService.setNx(lockKey);
        }
        if (!lock) {
            log.info("策略奖品库存加锁失败 {}", lockKey);
        }
        return lock;
    }



    @Override
    public void awardStockConsumeSendQueue(StrategyAwardStockKeyVO strategyAwardStockKeyVO) {
       //通过这个key来识别相对应的对列
        String queryKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_QUERY_KEY;
        //这个队列是通过redis实现的
        RBlockingQueue<StrategyAwardStockKeyVO> blockingQueue = redisService.getBlockingQueue(queryKey);
        //delayQueue只是对RBlockingQueue的一个装饰,在取消息时还是要用getBlockingQueue来取
        RDelayedQueue<StrategyAwardStockKeyVO> delayQueue = redisService.getDelayedQueue(blockingQueue);
        delayQueue.offer(strategyAwardStockKeyVO,3, TimeUnit.SECONDS);
    }

    @Override
    public StrategyAwardStockKeyVO takeQueueValue() {
        String queryKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_QUERY_KEY;
        RBlockingQueue<StrategyAwardStockKeyVO> delayedQueue = redisService.getBlockingQueue(queryKey);

        return delayedQueue.poll();
    }

    @Override
    public void updateStrategyAwardStock(Long strategyId, String awardId) {
        strategyAwardDao.updateStrategyAwardStock(strategyId,awardId);
    }

    @Override
    public StrategyAwardEntity queryStrategyAwardEntity(Long strategyId, Integer awardId) {
        // 优先从缓存获取
        String cacheKey = Constants.RedisKey.STRATEGY_AWARD_KEY + strategyId + Constants.UNDERLINE + awardId;
        StrategyAwardEntity strategyAwardEntity = redisService.getValue(cacheKey);
        if (null != strategyAwardEntity) return strategyAwardEntity;

        StrategyAward strategyAward = strategyAwardDao.queryStrategyAwardByStrategyIdAndAwardId(strategyId, awardId);
         strategyAwardEntity = StrategyAwardEntity.builder()
                .strategyId(strategyAward.getStrategyId())
                .awardId(strategyAward.getAwardId())
                .ruleModels(strategyAward.getRuleModels())
                .awardCount(strategyAward.getAwardCount())
                .awardCountSurplus(strategyAward.getAwardCountSurplus())
                .awardRate(strategyAward.getAwardRate())
                .awardTitle(strategyAward.getAwardTitle())
                .awardSubtitle(strategyAward.getAwardSubtitle())
                .sort(strategyAward.getSort())
                .build();
        return strategyAwardEntity;
    }

    @Override
    public Long queryStrategyIdByActivityId(Long activityId) {
        RaffleActivity activity= activityDao.queryRaffleActivityByActivityId(activityId);
        return activity.getStrategyId();
    }

    @Override
    public Integer queryTodayUserRaffleCount(String userId, Long strategyId) {
        // 活动ID
        Long activityId = raffleActivityDao.queryActivityIdByStrategyId(strategyId);
        // 封装参数
        RaffleActivityAccountDay raffleActivityAccountDayReq = new RaffleActivityAccountDay();
        raffleActivityAccountDayReq.setUserId(userId);
        raffleActivityAccountDayReq.setActivityId(activityId);
        raffleActivityAccountDayReq.setDay(raffleActivityAccountDayReq.currentDay());
        RaffleActivityAccountDay raffleActivityAccountDay = raffleActivityAccountDayDao.queryActivityAccountDayByUserId(raffleActivityAccountDayReq);
        if (null == raffleActivityAccountDay) return 0;
        // 总次数 - 剩余的，等于今日参与的
        return raffleActivityAccountDay.getDayCount() - raffleActivityAccountDay.getDayCountSurplus();

    }

    /**
     * 根据treeId找到对应treeNode上的ruleValue的值
     * @param treeId
     * @param ruleKey 标识单个节点的key
     * @return
     */
    @Override
    public Integer queryRuleValueByTreeIdAndRuleKey(String treeId,String ruleKey) {
        RuleTreeNode ruleTreeNode = ruleTreeNodeDao.queryRuleTreeNodeByTreeIdAndRuleKey(treeId,ruleKey);

        if (ruleTreeNode==null || ruleTreeNode.getRuleValue() ==null)
            return null;
        else return Integer.valueOf(ruleTreeNode.getRuleValue());

    }


    /**
     * 根据策略id查询对应的抽奖次数门槛及其对应的奖品范围
     * @param strategyId 策略id
     * @return 抽奖次数门槛及其对应的奖品范围 的列表
     */
    @Override
    public List<RuleWeightVO> queryAwardRuleWeightByStrategyId(Long strategyId) {
        List<RuleWeightVO> ruleWeightVOS = new ArrayList<>();
        StrategyRule strategyRule = strategyRuleDao.queryStrategyRuleByStrategyIdAndRuleModel(strategyId, DefaultLogicChainFactory.LogicModel.RULE_WEIGHT.getCode());
            //借助StrategyRuleEntity来获取map对象,方便操作
        StrategyRuleEntity strategyRuleEntity = StrategyRuleEntity.builder()
                    .ruleModel(strategyRule.getRuleModel())
                    .ruleValue(strategyRule.getRuleValue())
                    .build();
        Map<String, List<Integer>> ruleValuesMap = strategyRuleEntity.getRuleValuesMap();

        Set<String> ruleWeights = ruleValuesMap.keySet();
        for (String ruleWeight:ruleWeights){
            //查询各个权重对应奖品值
            List<Integer> singleRuleValueAwardIds = ruleValuesMap.get(ruleWeight);
            //得到awardList
            List<RuleWeightVO.Award> awardList = new ArrayList<>();
            for(Integer singleRuleValueAwardId : singleRuleValueAwardIds){
                StrategyAward strategyAward = strategyAwardDao.queryStrategyAwardByStrategyIdAndAwardId(strategyId, singleRuleValueAwardId);
                awardList.add(RuleWeightVO.Award.builder()
                        .awardId(strategyAward.getAwardId())
                        .awardTitle(strategyAward.getAwardTitle())
                        .build());
            }

            ruleWeightVOS.add(RuleWeightVO.builder()
                    .ruleValue(strategyRuleEntity.getRuleValue())
                    .weight(Integer.valueOf(ruleWeight.split(Constants.COLON)[0])) //权重值,即key用冒号分割的第一个
                    .awardIds(singleRuleValueAwardIds) //奖品id的list,即value
                    .awardList(awardList)
                    .build());

        }



        return ruleWeightVOS;
    }

    /**
     * 查询用户已经抽奖的次数
     * @param userId
     * @param strategyId
     * @return
     */
    @Override
    public Integer queryActivityAccountTotalUseCount(String userId, Long strategyId) {
        Long activityId = raffleActivityDao.queryActivityIdByStrategyId(strategyId);
        RaffleActivityAccount raffleActivityAccount = raffleActivityAccountDao.queryRaffleActivityAccountByUserId(RaffleActivityAccount.builder()
                .activityId(activityId)
                .userId(userId)
                .build());

        return raffleActivityAccount.getTotalCount() - raffleActivityAccount.getTotalCountSurplus();
    }

}
